package xyz.lwm.lazycat.jdkhttp;

import com.sun.net.httpserver.HttpServer;
import lombok.SneakyThrows;
import xyz.lwm.lazycat.utility.Assert;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.PathUtil;
import xyz.lwm.lazycat.utility.ThrowableUtil;
import xyz.lwm.lazycat.web.WebException;
import xyz.lwm.lazycat.web.WebServer;
import xyz.lwm.lazycat.web.handle.Context;
import xyz.lwm.lazycat.web.handle.HandlerType;
import xyz.lwm.lazycat.web.handle.JsonMapper;
import xyz.lwm.lazycat.web.route.Route;
import xyz.lwm.lazycat.web.route.RouteTable;
import xyz.lwm.lazycat.web.settings.ThreadPoolSetting;
import xyz.lwm.lazycat.web.settings.WebSetting;
import xyz.lwm.lazycat.web.util.ContextHolder;

import java.net.InetSocketAddress;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static xyz.lwm.lazycat.jdkhttp.JdkConstants.BLOCK_QUEUE_SIZE;
import static xyz.lwm.lazycat.jdkhttp.JdkConstants.CORE_POOL_SIZE;
import static xyz.lwm.lazycat.jdkhttp.JdkConstants.DEFAULT_PORT;
import static xyz.lwm.lazycat.jdkhttp.JdkConstants.KEEP_ALIVE_TIME;
import static xyz.lwm.lazycat.jdkhttp.JdkConstants.MAX_POOL_SIZE;
import static xyz.lwm.lazycat.utility.StrPool.STR_SLASH;

/**
 * jdk http server
 *
 * @author lwm
 */
public class JdkHttpServer implements WebServer {

    private final WebSetting webProperties;

    private final JsonMapper jsonMapper;

    private HttpServer httpServer;

    public JdkHttpServer(WebSetting webProperties, JsonMapper jsonMapper) {
        this.webProperties = webProperties;
        this.jsonMapper = jsonMapper;
    }

    @SneakyThrows
    @Override
    public void start(RouteTable routeTable) {
        // 创建 httpServer
        int port = webProperties.getServer().getPort();
        if (port <= 0) {
            port = DEFAULT_PORT;
        }
        httpServer = HttpServer.create(new InetSocketAddress(port), 0);

        // 创建线程池
        It<ThreadPoolSetting> poolIt = It.of(webProperties.getServer().getThreadPool());
        int corePoolSize = poolIt.let(ThreadPoolSetting::getCorePoolSize)
                .filter(i -> i >= CORE_POOL_SIZE).getOr(CORE_POOL_SIZE);
        int maxPoolSize = poolIt.let(ThreadPoolSetting::getCorePoolSize)
                .filter(i -> i >= MAX_POOL_SIZE).getOr(MAX_POOL_SIZE);
        int keepAliveTime = poolIt.let(ThreadPoolSetting::getCorePoolSize)
                .filter(i -> i >= KEEP_ALIVE_TIME).getOr(KEEP_ALIVE_TIME);
        int queueCapacity = poolIt.let(ThreadPoolSetting::getCorePoolSize)
                .filter(i -> i >= BLOCK_QUEUE_SIZE).getOr(BLOCK_QUEUE_SIZE);

        httpServer.setExecutor(new ThreadPoolExecutor(corePoolSize, maxPoolSize,
                keepAliveTime, TimeUnit.SECONDS, new ArrayBlockingQueue<>(queueCapacity)));

        // context path 处理
        String contextPath = webProperties.getServer().getContextPath();
        String prefix = STR_SLASH + PathUtil.normalizePath(contextPath);

        // 添加路由
        httpServer.createContext(STR_SLASH, httpExchange -> {

            JdkHttpRequest request = new JdkHttpRequest(httpExchange,
                    webProperties.getServer().getFileUpload());
            JdkHttpResponse response = new JdkHttpResponse(httpExchange);
            Context context = new Context(request, response, jsonMapper);

            ContextHolder.set(context);

            try {
                String path = request.getPath();
                HandlerType handlerType = getHandlerType(httpExchange.getRequestMethod());

                if (STR_SLASH.equals(path)) {
                    It.of(routeTable.get(handlerType, path))
                            .getOrThrow(() -> new WebException("not found route: " + path))
                            .getHandler().handle(context);

                } else if (path.startsWith(prefix)) {
                    // 获取请求路径
                    final String reqPath = path.substring(prefix.length());
                    Route route = routeTable.get(handlerType, reqPath);
                    Assert.isTrue(route != null, "not found route: {} {}", handlerType, reqPath);
                    // 处理路径参数
                    request.getPathParams().putAll(JdkParamUtil.parsePathParams(route.getPath(), reqPath));
                    // 处理请求
                    route.getHandler().handle(context);

                } else {
                    context.sendError(404, "not found");
                }

                httpExchange.close();

            } catch (Exception e) {
                context.json(ThrowableUtil.print(e));

            } finally {
                ContextHolder.clear();
            }
        });

        httpServer.start();
    }

    private HandlerType getHandlerType(String requestMethod) {
        try {
            return HandlerType.valueOf(requestMethod.toUpperCase());
        } catch (Exception e) {
            throw new WebException(requestMethod + " not support", e);
        }
    }

    @Override
    public void stop() {
        if (httpServer != null) {
            httpServer.stop(2);
        }
    }

}
